home *** CD-ROM | disk | FTP | other *** search
/ PC World 2008 September / PCWorld_2008-09_cd.bin / v cisle / sadanastroju / greasemonkey-0.8.20080609.0-fx.xpi / components / greasemonkey.js < prev   
Text File  |  2008-06-09  |  15KB  |  498 lines

  1. const CLASSNAME = "GM_GreasemonkeyService";
  2. const CONTRACTID = "@greasemonkey.mozdev.org/greasemonkey-service;1";
  3. const CID = Components.ID("{77bf3650-1cd6-11da-8cd6-0800200c9a66}");
  4.  
  5. const Cc = Components.classes;
  6. const Ci = Components.interfaces;
  7.  
  8. const appSvc = Cc["@mozilla.org/appshell/appShellService;1"]
  9.                  .getService(Ci.nsIAppShellService);
  10.  
  11. const gmSvcFilename = Components.stack.filename;
  12.  
  13. function alert(msg) {
  14.   Cc["@mozilla.org/embedcomp/prompt-service;1"]
  15.     .getService(Ci.nsIPromptService)
  16.     .alert(null, "Greasemonkey alert", msg);
  17. }
  18.  
  19. // Examines the stack to determine if an API should be callable.
  20. function GM_apiLeakCheck(apiName) {
  21.   var stack = Components.stack;
  22.  
  23.   do {
  24.     // Valid stack frames for GM api calls are: native and js when coming from
  25.     // chrome:// URLs and the greasemonkey.js component's file:// URL.
  26.     if (2 == stack.language) {
  27.       // NOTE: In FF 2.0.0.0, I saw that stack.filename can be null for JS/XPCOM
  28.       // services. This didn't happen in FF 2.0.0.11; I'm not sure when it
  29.       // changed.
  30.       if (stack.filename != null &&
  31.           stack.filename != gmSvcFilename &&
  32.           stack.filename.substr(0, 6) != "chrome") {
  33.         GM_logError(new Error("Greasemonkey access violation: unsafeWindow " +
  34.                     "cannot call " + apiName + "."));
  35.         return false;
  36.       }
  37.     }
  38.  
  39.     stack = stack.caller;
  40.   } while (stack);
  41.  
  42.   return true;
  43. }
  44.  
  45. var greasemonkeyService = {
  46.   _config: null,
  47.   get config() {
  48.     if (!this._config)
  49.       this._config = new Config();
  50.     return this._config;
  51.   },
  52.   browserWindows: [],
  53.   updater: null,
  54.  
  55.  
  56.   // nsISupports
  57.   QueryInterface: function(aIID) {
  58.     if (!aIID.equals(Ci.nsIObserver) &&
  59.         !aIID.equals(Ci.nsISupports) &&
  60.         !aIID.equals(Ci.nsISupportsWeakReference) &&
  61.         !aIID.equals(Ci.gmIGreasemonkeyService) &&
  62.         !aIID.equals(Ci.nsIWindowMediatorListener) &&
  63.         !aIID.equals(Ci.nsIContentPolicy)) {
  64.       throw Components.results.NS_ERROR_NO_INTERFACE;
  65.     }
  66.  
  67.     return this;
  68.   },
  69.  
  70.  
  71.   // nsIObserver
  72.   observe: function(aSubject, aTopic, aData) {
  73.     if (aTopic == "app-startup") {
  74.       this.startup();
  75.     }
  76.   },
  77.  
  78.  
  79.   // gmIGreasemonkeyService
  80.   registerBrowser: function(browserWin) {
  81.     var existing;
  82.  
  83.     for (var i = 0; existing = this.browserWindows[i]; i++) {
  84.       if (existing == browserWin) {
  85.         // NOTE: Unlocalised strings
  86.         throw new Error("Browser window has already been registered.");
  87.       }
  88.     }
  89.  
  90.     this.browserWindows.push(browserWin);
  91.   },
  92.  
  93.   unregisterBrowser: function(browserWin) {
  94.    var existing;
  95.  
  96.     for (var i = 0; existing = this.browserWindows[i]; i++) {
  97.       if (existing == browserWin) {
  98.         this.browserWindows.splice(i, 1);
  99.         return;
  100.       }
  101.     }
  102.  
  103.     throw new Error("Browser window is not registered.");
  104.   },
  105.  
  106.   domContentLoaded: function(wrappedContentWin, chromeWin) {
  107.     var unsafeWin = wrappedContentWin.wrappedJSObject;
  108.     var unsafeLoc = new XPCNativeWrapper(unsafeWin, "location").location;
  109.     var href = new XPCNativeWrapper(unsafeLoc, "href").href;
  110.     var scripts = this.initScripts(href);
  111.  
  112.     if (scripts.length > 0) {
  113.       this.injectScripts(scripts, href, unsafeWin, chromeWin);
  114.     }
  115.  
  116.     // Need to wait until well after startup for prefs store and extension
  117.     // manager to be initialized. First page load is a convenient place.
  118.     if (!this.updater) {
  119.       // Note: the param to this has to match the extension ID in install.rdf
  120.       this.updater = new ExtensionUpdater(
  121.           "{e4a8a97b-f2ed-450b-b12d-ee082ba24781}");
  122.       this.updater.updatePeriodically();
  123.     }
  124.   },
  125.  
  126.  
  127.   startup: function() {
  128.     Cc["@mozilla.org/moz/jssubscript-loader;1"]
  129.       .getService(Ci.mozIJSSubScriptLoader)
  130.       .loadSubScript("chrome://global/content/XPCNativeWrapper.js");
  131.  
  132.     Cc["@mozilla.org/moz/jssubscript-loader;1"]
  133.       .getService(Ci.mozIJSSubScriptLoader)
  134.       .loadSubScript("chrome://greasemonkey/content/prefmanager.js");
  135.  
  136.     Cc["@mozilla.org/moz/jssubscript-loader;1"]
  137.       .getService(Ci.mozIJSSubScriptLoader)
  138.       .loadSubScript("chrome://greasemonkey/content/utils.js");
  139.  
  140.     Cc["@mozilla.org/moz/jssubscript-loader;1"]
  141.       .getService(Ci.mozIJSSubScriptLoader)
  142.       .loadSubScript("chrome://greasemonkey/content/config.js");
  143.  
  144.     Cc["@mozilla.org/moz/jssubscript-loader;1"]
  145.       .getService(Ci.mozIJSSubScriptLoader)
  146.       .loadSubScript("chrome://greasemonkey/content/convert2RegExp.js");
  147.  
  148.     Cc["@mozilla.org/moz/jssubscript-loader;1"]
  149.       .getService(Ci.mozIJSSubScriptLoader)
  150.       .loadSubScript("chrome://greasemonkey/content/miscapis.js");
  151.  
  152.     Cc["@mozilla.org/moz/jssubscript-loader;1"]
  153.       .getService(Ci.mozIJSSubScriptLoader)
  154.       .loadSubScript("chrome://greasemonkey/content/xmlhttprequester.js");
  155.  
  156.     Cc["@mozilla.org/moz/jssubscript-loader;1"]
  157.       .getService(Ci.mozIJSSubScriptLoader)
  158.       .loadSubScript("chrome://greasemonkey/content/updater.js");
  159.  
  160.     //loggify(this, "GM_GreasemonkeyService");
  161.   },
  162.  
  163.   shouldLoad: function(ct, cl, org, ctx, mt, ext) {
  164.     var ret = Ci.nsIContentPolicy.ACCEPT;
  165.  
  166.     // block content detection of greasemonkey by denying GM
  167.     // chrome content, unless loaded from chrome
  168.     if (org && org.scheme != "chrome" && cl.scheme == "chrome" &&
  169.         cl.host == "greasemonkey") {
  170.       return Ci.nsIContentPolicy.REJECT_SERVER;
  171.     }
  172.  
  173.     // don't intercept anything when GM is not enabled
  174.     if (!GM_getEnabled()) {
  175.       return ret;
  176.     }
  177.  
  178.     // don't interrupt the view-source: scheme
  179.     // (triggered if the link in the error console is clicked)
  180.     if ("view-source" == cl.scheme) {
  181.       return ret;
  182.     }
  183.  
  184.     if (ct == Ci.nsIContentPolicy.TYPE_DOCUMENT &&
  185.         cl.spec.match(/\.user\.js$/)) {
  186.  
  187.       dump("shouldload: " + cl.spec + "\n");
  188.       dump("ignorescript: " + this.ignoreNextScript_ + "\n");
  189.  
  190.       if (!this.ignoreNextScript_) {
  191.         if (!this.isTempScript(cl)) {
  192.           var winWat = Cc["@mozilla.org/embedcomp/window-watcher;1"]
  193.             .getService(Ci.nsIWindowWatcher);
  194.  
  195.           if (winWat.activeWindow && winWat.activeWindow.GM_BrowserUI) {
  196.             winWat.activeWindow.GM_BrowserUI.startInstallScript(cl);
  197.             ret = Ci.nsIContentPolicy.REJECT_REQUEST;
  198.           }
  199.         }
  200.       }
  201.     }
  202.  
  203.     this.ignoreNextScript_ = false;
  204.     return ret;
  205.   },
  206.  
  207.   shouldProcess: function(ct, cl, org, ctx, mt, ext) {
  208.     return Ci.nsIContentPolicy.ACCEPT;
  209.   },
  210.  
  211.   ignoreNextScript: function() {
  212.     dump("ignoring next script...\n");
  213.     this.ignoreNextScript_ = true;
  214.   },
  215.  
  216.   isTempScript: function(uri) {
  217.     if (uri.scheme != "file") {
  218.       return false;
  219.     }
  220.  
  221.     var fph = Components.classes["@mozilla.org/network/protocol;1?name=file"]
  222.     .getService(Ci.nsIFileProtocolHandler);
  223.  
  224.     var file = fph.getFileFromURLSpec(uri.spec);
  225.     var tmpDir = Components.classes["@mozilla.org/file/directory_service;1"]
  226.     .getService(Components.interfaces.nsIProperties)
  227.     .get("TmpD", Components.interfaces.nsILocalFile);
  228.  
  229.     return file.parent.equals(tmpDir) && file.leafName != "newscript.user.js";
  230.   },
  231.  
  232.   initScripts: function(url) {
  233.     function testMatch(script) {
  234.       return script.enabled && script.matchesURL(url);
  235.     }
  236.  
  237.     return GM_getConfig().getMatchingScripts(testMatch);
  238.   },
  239.  
  240.   injectScripts: function(scripts, url, unsafeContentWin, chromeWin) {
  241.     var sandbox;
  242.     var script;
  243.     var logger;
  244.     var console;
  245.     var storage;
  246.     var xmlhttpRequester;
  247.     var resources;
  248.     var safeWin = new XPCNativeWrapper(unsafeContentWin);
  249.     var safeDoc = safeWin.document;
  250.  
  251.     // detect and grab reference to firebug console and context, if it exists
  252.     var firebugConsole = this.getFirebugConsole(unsafeContentWin, chromeWin);
  253.  
  254.     for (var i = 0; script = scripts[i]; i++) {
  255.       sandbox = new Components.utils.Sandbox(safeWin);
  256.  
  257.       logger = new GM_ScriptLogger(script);
  258.  
  259.       console = firebugConsole ? firebugConsole : new GM_console(script);
  260.  
  261.       storage = new GM_ScriptStorage(script);
  262.       xmlhttpRequester = new GM_xmlhttpRequester(unsafeContentWin,
  263.                                                  appSvc.hiddenDOMWindow);
  264.       resources = new GM_Resources(script);
  265.  
  266.       sandbox.window = safeWin;
  267.       sandbox.document = sandbox.window.document;
  268.       sandbox.unsafeWindow = unsafeContentWin;
  269.  
  270.       // hack XPathResult since that is so commonly used
  271.       sandbox.XPathResult = Ci.nsIDOMXPathResult;
  272.  
  273.       // add our own APIs
  274.       sandbox.GM_addStyle = function(css) { GM_addStyle(safeDoc, css) };
  275.       sandbox.GM_log = GM_hitch(logger, "log");
  276.       sandbox.console = console;
  277.       sandbox.GM_setValue = GM_hitch(storage, "setValue");
  278.       sandbox.GM_getValue = GM_hitch(storage, "getValue");
  279.       sandbox.GM_getResourceURL = GM_hitch(resources, "getResourceURL");
  280.       sandbox.GM_getResourceText = GM_hitch(resources, "getResourceText");
  281.       sandbox.GM_openInTab = GM_hitch(this, "openInTab", unsafeContentWin);
  282.       sandbox.GM_xmlhttpRequest = GM_hitch(xmlhttpRequester,
  283.                                            "contentStartRequest");
  284.       sandbox.GM_registerMenuCommand = GM_hitch(this,
  285.                                                 "registerMenuCommand",
  286.                                                 unsafeContentWin);
  287.  
  288.       sandbox.__proto__ = safeWin;
  289.  
  290.       var contents = script.textContent;
  291.  
  292.       var requires = [];
  293.       var offsets = [];
  294.       var offset = 0;
  295.  
  296.       script.requires.forEach(function(req){
  297.         var contents = req.textContent;
  298.         var lineCount = contents.split("\n").length;
  299.         requires.push(contents);
  300.         offset += lineCount;
  301.         offsets.push(offset);
  302.       })
  303.       script.offsets = offsets;
  304.  
  305.       var scriptSrc = "\n" + // error line-number calculations depend on these
  306.                          requires.join("\n") +
  307.                          "\n" +
  308.                          contents +
  309.                          "\n";
  310.       if (!this.evalInSandbox(scriptSrc, url, sandbox, script))
  311.         this.evalInSandbox("(function(){"+ scriptSrc +"})()",
  312.                            url, sandbox, script);
  313.     }
  314.   },
  315.  
  316.   registerMenuCommand: function(unsafeContentWin, commandName, commandFunc,
  317.                                 accelKey, accelModifiers, accessKey) {
  318.     var command = {name: commandName,
  319.                    accelKey: accelKey,
  320.                    accelModifiers: accelModifiers,
  321.                    accessKey: accessKey,
  322.                    doCommand: commandFunc,
  323.                    window: unsafeContentWin };
  324.  
  325.     for (var i = 0; i < this.browserWindows.length; i++) {
  326.       this.browserWindows[i].registerMenuCommand(command);
  327.     }
  328.   },
  329.  
  330.   openInTab: function(unsafeContentWin, url) {
  331.     var unsafeTop = new XPCNativeWrapper(unsafeContentWin, "top").top;
  332.  
  333.     for (var i = 0; i < this.browserWindows.length; i++) {
  334.       this.browserWindows[i].openInTab(unsafeTop, url);
  335.     }
  336.   },
  337.  
  338.   evalInSandbox: function(code, codebase, sandbox, script) {
  339.     if (!(Components.utils && Components.utils.Sandbox)) {
  340.       var e = new Error("Could not create sandbox.");
  341.       GM_logError(e, 0, e.fileName, e.lineNumber);
  342.     } else {
  343.       try {
  344.         // workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=307984
  345.         var lineFinder = new Error();
  346.         Components.utils.evalInSandbox(code, sandbox);
  347.       } catch (e) {
  348.         if ("return not in function" == e.message) // pre-0.8 GM compat:
  349.           return false; // this script depends on the function enclosure
  350.  
  351.         // try to find the line of the actual error line
  352.         var line = e.lineNumber;
  353.         if (4294967295 == line) {
  354.           // Line number is reported as max int in edge cases.  Sometimes
  355.           // the right one is in the "location", instead.  Look there.
  356.           if (e.location && e.location.lineNumber) {
  357.             line = e.location.lineNumber;
  358.           } else {
  359.             // Reporting max int is useless, if we couldn't find it in location
  360.             // either, forget it.  Value of 0 isn't shown in the console.
  361.             line = 0;
  362.           }
  363.         }
  364.  
  365.         if (line) {
  366.           var err = this.findError(script, line - lineFinder.lineNumber - 1);
  367.           GM_logError(
  368.             e, // error obj
  369.             0, // 0 = error (1 = warning)
  370.             err.uri,
  371.             err.lineNumber
  372.           );
  373.         } else {
  374.           GM_logError(
  375.             e, // error obj
  376.             0, // 0 = error (1 = warning)
  377.             script.fileURL,
  378.             0
  379.           );
  380.         }
  381.       }
  382.     }
  383.     return true; // did not need a (function() {...})() enclosure.
  384.   },
  385.  
  386.   findError: function(script, lineNumber){
  387.     var start = 0;
  388.     var end = 1;
  389.  
  390.     for (var i = 0; i < script.offsets.length; i++) {
  391.       end = script.offsets[i];
  392.       if (lineNumber < end) {
  393.         return {
  394.           uri: script.requires[i].fileURL,
  395.           lineNumber: (lineNumber - start)
  396.         };
  397.       }
  398.       start = end;
  399.     }
  400.  
  401.     return {
  402.       uri: script.fileURL,
  403.       lineNumber: (lineNumber - end)
  404.     };
  405.   },
  406.  
  407.   getFirebugConsole: function(unsafeContentWin, chromeWin) {
  408.     var firebugConsoleCtor = null;
  409.     var firebugContext = null;
  410.  
  411.     if (chromeWin && chromeWin.FirebugConsole) {
  412.       firebugConsoleCtor = chromeWin.FirebugConsole;
  413.       firebugContext = chromeWin.top.TabWatcher
  414.         .getContextByWindow(unsafeContentWin);
  415.  
  416.       // on first load (of multiple tabs) the context might not exist
  417.       if (!firebugContext) firebugConsoleCtor = null;
  418.     }
  419.  
  420.     if (firebugConsoleCtor && firebugContext) {
  421.       return new firebugConsoleCtor(firebugContext, unsafeContentWin);
  422.     } else {
  423.       return null;
  424.     }
  425.   }
  426. };
  427.  
  428. greasemonkeyService.wrappedJSObject = greasemonkeyService;
  429.  
  430. //loggify(greasemonkeyService, "greasemonkeyService");
  431.  
  432.  
  433.  
  434. /**
  435.  * XPCOM Registration goop
  436.  */
  437. var Module = new Object();
  438.  
  439. Module.registerSelf = function(compMgr, fileSpec, location, type) {
  440.   compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
  441.   compMgr.registerFactoryLocation(CID,
  442.                                   CLASSNAME,
  443.                                   CONTRACTID,
  444.                                   fileSpec,
  445.                                   location,
  446.                                   type);
  447.  
  448.   var catMgr = Cc["@mozilla.org/categorymanager;1"]
  449.                  .getService(Ci.nsICategoryManager);
  450.  
  451.   catMgr.addCategoryEntry("app-startup",
  452.                           CLASSNAME,
  453.                           CONTRACTID,
  454.                           true,
  455.                           true);
  456.  
  457.   catMgr.addCategoryEntry("content-policy",
  458.                           CONTRACTID,
  459.                           CONTRACTID,
  460.                           true,
  461.                           true);
  462. };
  463.  
  464. Module.getClassObject = function(compMgr, cid, iid) {
  465.   if (!cid.equals(CID)) {
  466.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  467.   }
  468.  
  469.   if (!iid.equals(Ci.nsIFactory)) {
  470.     throw Components.results.NS_ERROR_NO_INTERFACE;
  471.   }
  472.  
  473.   return Factory;
  474. };
  475.  
  476. Module.canUnload = function(compMgr) {
  477.   return true;
  478. };
  479.  
  480.  
  481. var Factory = new Object();
  482.  
  483. Factory.createInstance = function(outer, iid) {
  484.   if (outer != null) {
  485.     throw Components.results.NS_ERROR_NO_AGGREGATION;
  486.   }
  487.  
  488.   return greasemonkeyService;
  489. };
  490.  
  491.  
  492. function NSGetModule(compMgr, fileSpec) {
  493.   return Module;
  494. }
  495.  
  496. //loggify(Module, "greasemonkeyService:Module");
  497. //loggify(Factory, "greasemonkeyService:Factory");
  498.